<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
 * FormativeLab
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		FormativeLab
 * @author		FormativeLab Dev Team
 * @copyright	Copyright (c) 2011 - 2013, FormativeLab, Inc.
 * @license		http://www.formativesoft.com/license/
 * @link		http://www.formativesoft.com/
 * @since		Version 2.0
 * @filesource ./application/helpers/constraints_helper.php
 */

// ------------------------------------------------------------------------

/**
 * FormativeLab Constraints Helpers
 *
 * @package		FormativeLab
 * @subpackage	Helpers
 * @category	Helpers
 * @author		Formative Soft Ltd
 * @link		http://www.formativesoft.com/
 */

// ------------------------------------------------------------------------

/**
 * Constraints class use for validation of domain/model/entity object.
 * Its helpfull for preventing vulnerablity and malware access of persist
 * oject via crud operation. Specially for Insert and Update.
 **/ 
 if(!class_exists('Constraints')){
	class Constraints {
	
		private $lang;
		private $input;
		private $output;
		private $session;
	
		private $messages = array();
		private $entity;
		private $skip;
		
		/**
		 * Common validation constraints declarations are goes here. These 
		 * constraints are usefull for validate domain/model/entity object.
		 **/
		const ADDRESS_BN_OP		= '/^[\p{Bengali}\d\x{0964},.#:\-\/ ]{0,200}$/u';
		const ADDRESS_BN_RE		= '/^[\p{Bengali}\d\x{0964},.#:\-\/ ]{1,200}$/u';
		const ADDRESS_EN_OP		= '/^[\w,.#:\-\/ ]{0,100}$/';
		const ADDRESS_EN_RE		= '/^[\w,.#:\-\/ ]{1,100}$/';
		
		const BANGLA_OP			= '/^[\p{Bengali}\x{0964},.#:\-\/ ]{0,100}$/u';
		const BANGLA_RE			= '/^[\p{Bengali}\x{0964},.#:\-\/ ]{1,100}$/u';
		const BLODD_BN			= '/^[\x{098F}|\x{09AC}\x{09BF}|\x{098F}\x{09AC}\x{09BF}|\x{0993}]{1,3}[+-]{1}$/u';
		const BLOOD_EN			= '/^[A|a|B|b|AB|ab|O|o]{1,2}[+-]{1}$/';		
		
		const CONTINENT_BN		= '/^[\p{Bengali}.\- ]{0,100}$/u';
		const CONTINENT_EN		= '/^[a-zA-Z.\- ]{3,45}$/';
		const COUNTRY_BN		= '/^[\p{Bengali}.\- ]{0,100}$/u';
		const COUNTRY_EN		= '/^[a-zA-Z.\- ]{2,45}$/';
		
		
		const COUNTY_BN			= '/^[\p{Bengali}.\- ]{0,100}$/u';	
		const COUNTY_EN			= '/^[a-zA-Z.\- ]{2,45}$/';
		const CONTACT			= NULL;
		const CONTACT_NO		= '/^[\d]{5,16}|([\+]{1}[\d]{10,15})|([\+]{1}|[0]{2})[\(]{1}[\d]{2,3}[\)]{1}[\d]{8,13}$/';		
			
		const DATE_FUTURE		= NULL;
		const DATE_PAST			= NULL;
		const DATA_SIZE			= NULL;		
		const DIGITS			= NULL;
		
		const ENGLISH_OP		= '/^[\w,.#:\-/ ]{0,45}$/';
		const ENGLISH_RE		= '/^[\w,.#:\-/ ]{1,45}$/';
		const EMAIL				= NULL;
		const FIRST_NAME_BN_OP	= '/^[\p{Bengali}.\- ]{0,100}$/u';
		
		const FIRST_NAME_BN_RE	= '/^[\p{Bengali}.\- ]{1,100}$/u';
		const FIRST_NAME_EN_OP	= '/^[a-zA-Z.\- ]{0,45}$/';		
		const FIRST_NAME_EN_RE	= '/^[a-zA-Z\- ]{1,45}$/';				
		const FORM				= '/^F[0-9]{2}[B|I|R|T][0-9]{6}$/';
		
		const FULL_NAME_BN_OP	= '/^[\p{Bengali}.\- ]{0,100}$/u';		
		const FULL_NAME_BN_RE	= '/^[\p{Bengali}.\- ]{1,100}$/u';
		const FULL_NAME_EN_OP	= '/^[a-zA-Z.\- ]{0,45}$/';		
		const FULL_NAME_EN_RE	= '/^[a-zA-Z.\- ]{2,45}$/';			
		
		const LABEL_FORM		= '/^[\w]{3,30}$/';		
		const LABEL_REPORT		= '/^[L]{1}[B]{1}[L]{1}[_]{1}[A-Z0-9]{2,30}$/';
		const LAST_NAME_BN_OP	= '/^[\p{Bengali}.\- ]{0,100}$/u';		
		const LAST_NAME_BN_RE	= '/^[\p{Bengali}.\- ]{1,100}$/u';
		
		const LAST_NAME_EN_OP	= '/^[a-zA-Z.\- ]{0,45}$/';		
		const LAST_NAME_EN_RE	= '/^[a-zA-Z.\- ]{2,45}$/';		
		const LENGTH			= NULL;		
		const MATCH				= NULL;
		
		const MIDDLE_NAME_BN_OP	= '/^[\p{Bengali}.\- ]{0,100}$/u';		
		const MIDDLE_NAME_BN_RE	= '/^[\p{Bengali}.\- ]{1,100}$/u';
		const MIDDLE_NAME_EN_OP	= '/^[a-zA-Z.\- ]{0,45}$/';		
		const MIDDLE_NAME_EN_RE	= '/^[a-zA-Z.\- ]{2,45}$/';
		
		const NAME_BN_OP		= "/^[\p{Bengali}.\- ]{0,100}$/u";		
		const NAME_BN_RE		= '/^[\p{Bengali}.\- ]{1,100}$/u';
		const NAME_EN_OP		= '/^[a-zA-Z.\- ]{0,45}$/';		
		const NAME_EN_RE		= '/^[a-zA-Z.\- ]{2,45}$/';	
		
		const NICK_NAME_BN_OP	= '/^[\p{Bengali}.\- ]{0,100}$/u';		
		const NICK_NAME_BN_RE	= '/^[\p{Bengali}.\- ]{1,100}$/u';
		const NICK_NAME_EN_OP	= '/^[a-zA-Z.\- ]{0,45}$/';		
		const NICK_NAME_EN_RE	= '/^[a-zA-Z.\- ]{2,45}$/';			
		
		const NOT_EMPTY			= NULL;		
		const NOT_NULL			= NULL;
		const PERMISSION		= NULL;
		const POST_CODE 		= '/^[\d]{0,4}$/';
		
		const REPORT			= '/^R[0-9]{2}[B|I|T][0-9]{6}$/';		
		const SUB_REPORT		= '/^R[0-9]{2}[B|I|T][0-9]{6}[X][0-9]{2}$/';
		const UNIQUE			= NULL;
		const UPDATED			= NULL;
		
		static $helper			= array('language');
		
		/**
		 * __construct() function is an constructor with one required and another 
		 * optional parameters. This constructor is use for initialized class
		 * for validation check.
		 *
		 * @access		public
		 * @property	entity
		 * @property	skip
		 **/
		public function __construct(&$entity, $skip = array()){
			$codeIgniter		= &get_instance();
			$codeIgniter->load->helper(self::$helper);			
			
			$this->session 		= &$codeIgniter->session;
			$this->output 		= &$codeIgniter->output;
			$this->input  		= &$codeIgniter->input;
			$this->lang			= &$codeIgniter->lang;
			
			$this->entity 		= &$entity;
			$this->skip			= $skip;
			
			$this->loadLanguage();
		}
		
		/**
		 * loadLanguage() is an function without any parameters. This method is use for
		 * internationalizaition messaging for validation message
		 *
		 * @access		private
		 * @return		void
		 **/
		private function loadLanguage($local = 'english'){
			$entity				= &$this->entity;
			$class				= get_class($entity);
			loadLanguage($class, $local);
		}
		
		/**
		 * init() is an function with one required and another optional 
		 * parameters. This method is use for initialized class for 
		 * validation check.
		 *
		 * @static
		 * @access		public
		 * @property	entity
		 * @property	skip
		 * @return		Constraints
		 **/
		public static function init(&$entity, $skip = array()){
			$validator		= new Constraints($entity, $skip);
			return $validator;
		}
		
		/**
		 * validate() is an function with no parameters. This method is use 
		 * check domain/model/entity object is valid or not.
		 *
		 * @access		public
		 * @return		bool
		 **/
		public function validate(){
			$entity			= &$this->entity;
			$constraints	= $entity->getConstraints();
			$validateFlag	= true;
			
			foreach($constraints as $property => $validators){
				foreach($validators as $constraint => $message){
					$pattern	= constant("self::$constraint");
					$value		= $entity->{$property};
					if($pattern === NULL){
						$flag	= $this->checkConstraint(
							$property, 
							$constraint,
							$message
						);
						$validateFlag = $validateFlag && $flag;
					}else if(!preg_match($pattern, $value)){
						$this->appendMessage($message);
						$validateFlag = $validateFlag && false;
					}
				}
			}
			return $validateFlag;
		}
		
		/**
		 * getMessages() is an function without any parameter. This method is use 
		 * for get validation messages.
		 *
		 * @access		public
		 * @return		array
		 **/
		public function getMessages(){
			return $this->messages;
		}
		
		/**
		 * appendMessage() is an function with an parameter. This method is use 
		 * for append validation message.
		 *
		 * @access		private
		 * @param		message
		 * @return		void
		 **/
		private function appendMessage($message){
			$errorMessage		= $this->lang->line($message);
			$this->messages[] 	= parseUnicodedHexString($errorMessage);
		}
		
		/**
		 * checkConstraint() is an function with three required parameters. 
		 * This method is use for constraint with additional matrix/factor.
		 * Its return bool value according to the condition.
		 *
		 * @access		private
		 * @param		value
		 * @param		constraint
		 * @param		matrix
		 * @return		bool
		 **/
		private function checkConstraint($property, $constraint, $matrix){
			$entity		= &$this->entity;
			$value		= $entity->{$property};
			
			switch($constraint){
				case 'CONTACT' 			: return $this->checkContact($value, $matrix); 			break;
				case 'DATE_FUTURE' 		: return $this->checkFutureDate($value, $matrix); 		break;
				case 'DATE_PAST'		: return $this->checkPreviousDate($value, $matrix); 	break;
				case 'DATA_SIZE' 		: return $this->checkDataSize($value, $matrix); 		break;
				case 'DIGITS'			: return $this->checkDigits($value, $matrix); 			break;
				case 'EMAIL'			: return $this->checkEmail($value, $matrix); 			break;
				case 'LENGTH'			: return $this->checkLength($value, $matrix); 			break;
				case 'MATCH'			: return $this->checkMatch($value, $matrix); 			break;
				case 'NOT_EMPTY'		: return $this->checkNotEmpty($value, $matrix);			break;
				case 'NOT_NULL'			: return $this->checkNotNull($value, $matrix);			break;
				case 'PERMISSION'		: return $this->checkPermission($value, $matrix);		break;
				case 'UNIQUE'			: return $this->checkUnique($property, $matrix);		break;
				case 'UPDATED'			: return $this->checkUpdated($property, $matrix);		break;		
			}
			return false;
		}
		
		/**
		 * checkContact() is an function with an required parameter. This method is use 
		 * to check valid contact such as email, phone or mobile no.
		 *
		 * @static
		 * @access		private
		 * @param		contact
		 * @return		bool
		 **/
		private function checkContact($contact, $message){
			if(!self::isContact($contact)){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkFutureDate() is an function with two required parameters. This method is used
		 * to check valid future date time. The second parameter contains an associative key 
		 * interval which one hold DateInterval String pattern such as P7Y5M4DT4H3M2S. Where P
		 * for pattern and Y, M, D, T, H, M, S are as repectively years, months, days, time,
		 * hours, minuties and seconds.
		 *
		 * @access		private
		 * @param		future
		 * @param		matrix
		 * @return		bool
		 **/
		private function checkFutureDate(DateTime $future, Array $matrix){
			$interval		= 'P0Y0M0DT0H0M0S';
			$message		= '';						
			
			foreach($matrix as $key => $value){
				switch($key){
					case 'message'	: $message	= $value; break;
					case 'interval'	: $interval	= $value; break;
				}
			}
			
			$reference	= new DateTime();
			$reference->add(new DateInterval($interval));
			
			if($future < $reference){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkPreviousDate() is an function with two required parameters. This method is used
		 * to check valid previous date time. The second parameter contains an associative key 
		 * interval which one hold DateInterval String pattern such as P7Y5M4DT4H3M2S. Where P
		 * for pattern and Y, M, D, T, H, M, S are as repectively years, months, days, time,
		 * hours, minuties and seconds.
		 *
		 * @access		private
		 * @param		previous
		 * @param		matrix
		 * @return		bool
		 **/
		private function checkPreviousDate(DateTime $previous, Array $matrix){
			$interval		= 'P0Y0M0DT0H0M0S';	
			$message		= '';		
			
			foreach($matrix as $key => $value){
				switch($key){
					case 'message'	: $message	= $value; break;
					case 'interval'	: $interval	= $value; break;
				}
			}
			
			$reference	= new DateTime();
			$reference->sub(new DateInterval($interval));
			
			if($previous > $reference){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkDataSize() is an function with two required parameters. This method is used
		 * to check valid bytes array data. The second parameter contains an associative key 
		 * message, maxSize, minSize by which we could check valid bytes array data.
		 *
		 * @access		private
		 * @param		data
		 * @param		matrix
		 * @return		bool
		 **/
		private function checkDataSize(Array $data, Array $matrix){
			$message		= '';
			$maxSize		= 0;
			$minSize		= 0;
			$dataSize		= sizeof($data);
			$dataSize		= ($dataSize/1024);
			
			foreach($matrix as $key => $value){
				switch($key){
					case 'message'	: $message	= $value; break;
					case 'maxSize'	: $maxSize	= $value; break;
					case 'minSize'	: $minSize	= $value; break;
				}
			}
			
			if($dataSize < $minSize || $dataSize > $maxSize){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkDigits() is an function with two required parameters. This method is
		 * used to check valid digits. The second parameter contains an associative 
		 * key message, integer, fraction by which we could check valid digits.
		 *
		 * pattern = '/^(([\d]+?)|([.]{1}[\d]+?)|([\d]+?[.]{1}[\d]+?))$/'
		 *
		 * @access		private
		 * @param		digits
		 * @param		matrix
		 * @return		bool
		 **/
		private function checkDigits($digits, Array $matrix){
			$message		= '';
			$integer		= 0;
			$fraction		= 0;
		
			foreach($matrix as $key => $value){
				switch($key){
					case 'message'	: $message	= $value; break;
					case 'integer'	: $integer	= $value; break;
					case 'fraction'	: $fraction	= $value; break;
				}
			}
			
			if(is_numeric($digits)){
			
				$cinteger		= 0;
				$cfraction		= 0;
				$string			= is_string($digits) ? $digits : "{$digits}";
				$metaData		= explode('.', $string);
				
				foreach($metaData as $index => $value){
					switch($index){
						case 0: $cinteger	= $value; break;
						case 1: $cfraction	= $value; break;				
					}				
				}
				
				if($integer < $cinteger || $fraction < $cfraction){
					$this->appendMessage($message);
					return false;
				}
				
			}else{
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkEmail() is an function with two required parameters. This method is used
		 * to check valid email addresses. The second parameter contains message 
		 *
		 * @access		private
		 * @param		email
		 * @param		message
		 * @return		bool
		 **/
		private function checkEmail($email, $message){		
			if(!self::isEmail($email)){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkLength()  is an function with two required parameters. This method is
		 * used to check acceptable length. The second parameter contains an associative 
		 * key message, maxLength, minLength by which we could check max min range of 
		 * stirng.
		 *
		 * @access		private
		 * @param		value
		 * @param		matrix
		 * @return		bool
		 **/
		private function checkLength($value, Array $matrix){
			$message		= '';
			$maxLength		= 0;
			$minLength		= 0;
			$strLength		= strlen($value);
		
			foreach($matrix as $key => $value){
				switch($key){
					case 'message'		: $message		= $value; break;
					case 'maxLength'	: $maxLength	= $value; break;
					case 'minLength'	: $minLength	= $value; break;
				}
			}
			if($strLength < $minLength || $strLength > $maxLength){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkMatch()  is an function with two required parameters. This method is
		 * used to check valid digits. The second parameter contains an associative 
		 * key message, refProperty, by which we could confirm value with referenc
		 * propertky. It will be usefull for confirm password/amount.
		 *
		 * @access		private
		 * @param		value
		 * @param		matrix
		 * @return		bool
		 **/
		private function checkMatch($value, Array $matrix){
			$entity			= &$this->entity;
			$message		= '';
			$refProperty	= NULL;
		
			foreach($matrix as $key => $value){
				switch($key){
					case 'message'		: $message		= $value; break;
					case 'refProperty'	: $refProperty	= $value; break;
				}
			}
			
			$refValue  = property_exists($model, $refProperty)? $entity->{$refProperty} : NULL;
			
			if($value != $refValue){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkNotEmpty()  is an function with two required parameters. This method 
		 * is used to check not empty string. The second parameter contains message
		 *
		 * @access		private
		 * @param		value
		 * @param		message
		 * @return		bool
		 **/
		private function checkNotEmpty($value, $message){
			if(empty($value)){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkNotNull()  is an function with two required parameters. This method 
		 * is used to check not null object. The second parameter contains message
		 *
		 * @access		private
		 * @param		value
		 * @param		message
		 * @return		bool
		 **/
		private function checkNotNull($value, $message){
			if(is_null($value)){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkPermission()  is an function with two required parameters. This method 
		 * is used to check not null object. The second parameter contains message
		 * CURD For Create Update Read Delete
		 * D U C R  CURD
		 * ===================
		 * 0 0 0 1 = 1  [__R_]
		 * 0 0 1 0 = 2  [C___]
		 * 0 0 1 1 = 3  [C_R_]
		 * 0 1 0 1 = 5  [_UR_]
		 * 0 1 1 1 = 7  [CUR_]
		 * 1 0 0 1 = 9  [__RD]
		 * 1 0 1 1 = 11 [CR_D]
		 * 1 1 0 1 = 13 [_URD]
		 * 1 1 1 1 = 15 [CURD]
		 *
		 * @access		private
		 * @param		value
		 * @param		message
		 * @return		bool
		 **/
		private function checkPermission($value, $message){
			$permission = array( 1, 2, 3, 5, 7, 9, 11, 13, 15);
			if(!in_array($value, $permission)){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
		
		/**
		 * checkUnique()  is an function with two required parameters. This method 
		 * is used to unique check of persis object. The second parameter contains 
		 * message
		 *
		 * @access		private
		 * @param		property
		 * @param		message
		 * @return		bool
		 **/
		private function checkUnique($property, $message){
			$entity		= &$this->entity;
			$value		= $entity->{$property};			
			$field		= getFieldByProperty($entity, $property);			
			$idProperty = getIdProperty($entity);
			$id			= $entity->{$idProperty};
			
			if(!is_null($value)){
				$entity->db->select('*');
				$entity->db->from($entity::ENTITY);
				$entity->db->where("{$field} =", $value);
				if($id){
					$entity->db->where_not_in($entity::ID_FIELD, array($id));
				}
				$count = $entity->db->count_all_results();
				if(count != 0){
					$this->appendMessage($message);
					return false;
				}
			}
			return true;
		}
		
		/**
		 * checkUpdated()  is an function with two required parameters. This method 
		 * is used for updated check of persis object. The second parameter contains 
		 * message
		 *
		 * @access		private
		 * @param		property
		 * @param		message
		 * @return		bool
		 **/
		private function checkUpdated($property, $message){
			$entity		= &$this->entity;
			$value		= $entity->{$property};
			if(false){
				$this->appendMessage($message);
				return false;
			}
			return true;
		}
				
		/**
		 * isContact() is an function with an required parameter. This method is use 
		 * to check valid contact such as email, phone or mobile no.
		 *
		 * @static
		 * @access		public
		 * @param		contact
		 * @return		bool
		 **/
		public static function isContact($contact){
			return self::isContactNo($contact) || self::isEmail($contact);
		}
		
		/**
		 * isContactNo() is an function with an required parameter. This method is use 
		 * to check valid contactNo.
		 *
		 * @static
		 * @access		public
		 * @param		contactNo
		 * @return		bool
		 **/
		public static function isContactNo($contactNo){
			return preg_match($contactNo, self::CONTACT_NO);
		}

		/**
		 * isEmail() is an function with an required parameter. This method is use 
		 * to check valid email addresses.
		 *
		 * @static
		 * @access		public
		 * @param		email
		 * @return		bool
		 **/
		public static function isEmail($email){
			return filter_var($email, FILTER_VALIDATE_EMAIL);
		}
		
		/**
		 * isURL() is an function with an required parameter. This method is use 
		 * to check valid URL/URI.
		 *
		 * @static
		 * @access		public
		 * @param		url
		 * @return		bool
		 **/
		public static function isURL($url){
			return filter_var($url, FILTER_VALIDATE_URL);
		}
		
		/**
		 * isIP() is an function with an required parameter. This method is use 
		 * to check valid IP addresses.
		 *
		 * @static
		 * @access		public
		 * @param		url
		 * @return		bool
		 **/
		public static function isIP($ip){
			return filter_var($itp, FILTER_VALIDATE_IP);
		}
		
		/**
		 * isPrivateIP() is an function with an required parameter. This method is use 
		 * to check valid private IP addresses.
		 *
		 * @static
		 * @access		public
		 * @param		url
		 * @return		bool
		 **/
		public static function isPrivateIP($ip){
			return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE);
		}
		
		/**
		 * isReservedIP() is an function with an required parameter. This method is use 
		 * to check valid reserved IP addresses.
		 *
		 * @static
		 * @access		public
		 * @param		url
		 * @return		bool
		 **/
		public static function isReservedIP($ip){
			return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE);
		}
		
		/**
		 * isPrivateReservedIP() is an function with an required parameter. This method is use 
		 * to check valid private or reserved IP addresses.
		 *
		 * @static
		 * @access		public
		 * @param		url
		 * @return		bool
		 **/
		public static function isPrivateReservedIP($ip){
			return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
		}
	} 
 }

// ------------------------------------------------------------------------

/**
 * validate() is an function with one required and another optional 
 * parameters. This method is use for initialized class for 
 * validation check.
 *
 * @access		public
 * @property	entity
 * @property	skip
 * @return		bool
 **/
if (!function_exists('validate')) {
    function validate(&$entity, $skip = array()){
        $validator		= Constraints::init($entity, $skip);        
        return $validator->validate();
    }
}

/* End of file constraints_helper.php */
/* Location: ./application/helpers/constraints_helper.php */